Skip to content

fix(daemon): scope pairwise a2a transcript reads to the session's own agent - #969

Merged
Poytr1 merged 1 commit into
mainfrom
claude/a2a-pairwise-transcript
Aug 14, 2026
Merged

fix(daemon): scope pairwise a2a transcript reads to the session's own agent#969
Poytr1 merged 1 commit into
mainfrom
claude/a2a-pairwise-transcript

Conversation

@Poytr1

@Poytr1 Poytr1 commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

What

Fixes #967. Every postless toAgent child of one caller shares the synthetic a2a:<caller> channel + the caller's thread, so all of a caller's pairwise sessions land in one physical transcript thread — and the §8.5 catch-up and turn-context refresh, built for genuinely shared conversations, replayed siblings' private deliveries (role assignments, canaries included) and reports into each child's prompts. Measured in the webchat Werewolf real run (#941): a player's night prompt carried the referee's role deliveries to other players.

The niche fix

The sessions are pairwise; the reads now are too — the coordinate scheme, session keys, wire, and every genuinely shared conversation are untouched:

  • isSyntheticA2aChannel (cp-collab-routes, beside the coordinate minting; the transcript channel key is prefix-preserving so it works on both raw and keyed forms);
  • the store gains transcriptSinceForAgent / transcriptSinceRevisionForAgent — the same sender/recipient/transcript_recipient delivery scope the console session views already use, factored into one shared AGENT_DELIVERY_SCOPE_SQL (the three existing inline copies now reuse it, byte-identical SQL);
  • the three context seams route through the scoped reads exactly when the session sits on a synthetic a2a channel: the §8.5 catch-up (session-manager), the thread-context refresh (scopeReadsToAgent), and localInvalidatingEvents.

Every a2a writer already stamps recipient (trigger append, observed-inbound, coalesce append), so a pair's own rows are all still visible to it.

Tests

  • New packages/daemon/test/a2a-transcript-privacy.test.ts — two children of one caller, two rounds of private deliveries + replies: each child sees its own canary, never the sibling's delivery or reply, across every prompt of every turn. Red without the fix (the sibling's canary appears verbatim in the child's second-turn catch-up), green with it.
  • Extended the scripted webchat Werewolf gate: the runner now audits every player prompt (conversation AND pairwise sessions) for role canaries the player's role does not hold — canaryCrossVisibility pinned 0 in both CI games. This is the live-topology regression probe: role secrecy in the single-conversation game now actually holds.
  • Unmodified: daemon webchat + feat(daemon): let webchat agent posts continue a multi-agent conversation (#549 parity) #906 continuation 47, session-manager 95, daemon-transcript, eval:collab:contracts 119, eval:parity 23, full typecheck + lint.

Expectations were only extended (the two new pins); nothing weakened.

🤖 Generated with Claude Code

… agent (#967)

Every postless toAgent child of one caller shares the synthetic a2a:<caller>
channel + the caller's thread, so all pairwise sessions of one caller land in
ONE physical transcript thread — and the §8.5 catch-up and turn-context
refresh, built for genuinely shared conversations, replayed SIBLINGS' private
deliveries (role assignments, canaries included) and reports into each
child's prompts. Measured in the webchat Werewolf real run (#941).

The sessions are pairwise; the reads now are too. isSyntheticA2aChannel
(cp-collab-routes, beside the coordinate minting) marks the synthetic
channel; the store gains transcriptSince(Revision)ForAgent — the same
sender/recipient/transcript_recipient delivery scope the console session
views already use, factored into one shared predicate — and the three
context seams (§8.5 catch-up in session-manager, the thread-context refresh,
localInvalidatingEvents) route through the scoped reads exactly when the
session sits on a synthetic a2a channel. Every a2a writer already stamps
recipient, so a pair's own rows are unaffected. Ordinary shared conversations
(channels, webchat rosters, co-hosted participants) are untouched.

Pinned by packages/daemon/test/a2a-transcript-privacy.test.ts (red without
the fix: the sibling's canary appears in the child's prompt) and, at game
level, by the new canaryCrossVisibility=0 audit in the scripted webchat
Werewolf gate (every player prompt scanned across conversation AND pairwise
sessions).

Closes #967.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@agentconnect-md-test agentconnect-md-test Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at d1a87a7d1f43e8d18bc278a6b0199988a208ba7d. I found no blocking regression in this revision.

The change applies the existing per-agent delivery predicate consistently to the synthetic a2a: session’s initial catch-up, turn-context refresh, and late local invalidation fence, while leaving genuinely shared conversations on the existing unscoped path. Directed rows remain visible through recipient/transcript_recipient, and each child’s own output remains visible through sender. The focused privacy test and Werewolf canary audit cover sibling delivery and reply leakage across subsequent prompts.

Verification performed: exact synthetic-merge parents matched the trusted base/head; all changed TypeScript files parsed and matched repository formatting; the factored SQL predicate was smoke-tested for sender, direct-recipient, delivery-table, and peer-internal-row behavior. I could not run the Vitest suite because this isolated workspace has no installed dependencies.

sent by review-bot (Codex · gpt-5.6-sol) · open in session

@Poytr1
Poytr1 merged commit 0d2254e into main Aug 14, 2026
11 checks passed
@Poytr1
Poytr1 deleted the claude/a2a-pairwise-transcript branch August 14, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

a2a: all of one caller's pairwise child sessions share one transcript thread — siblings can read each other's private deliveries via context refresh

1 participant